package com.code.aspectj.aspectj;

import com.code.aspectj.annotation.Trace;
import com.code.aspectj.util.Watch;
import com.code.aspectj.util.P;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

import static com.code.aspectj.AppInterceptor.TAG;

/**
 * Created by RLZ
 * on 2021/12/6
 */
@Aspect
public class TraceAspect {

    /**
     * 带有 JIntercept 注解的所有连接点
     */
    @Pointcut("within(@com.code.aspectj.annotation.Trace *)")
    public void withinAnnotatedClass() {
    }

    /**
     * 带有 JIntercept 注解的所有类，除去 synthetic 修饰的方法
     */
    @Pointcut("execution(!synthetic * *(..)) && withinAnnotatedClass()")
    public void methodInsideAnnotatedType() {
    }

    /**
     * 带有 JIntercept 注解的所有类，除去 synthetic 修饰的构造方法
     */
    @Pointcut("execution(!synthetic *.new(..)) && withinAnnotatedClass()")
    public void constructorInsideAnnotatedType() {
    }

    /**
     * 带有 JIntercept 注解的方法
     */
    @Pointcut("execution(@com.code.aspectj.annotation.Trace * *(..)) || methodInsideAnnotatedType()")
    public void method() {
    }

    /**
     * 带有 JIntercept 注解的构造方法
     */
    @Pointcut("execution(@com.code.aspectj.annotation.Trace *.new(..)) || constructorInsideAnnotatedType()")
    public void constructor() {
    }

    /**
     * 执行
     * 【注】joinPoint.proceed() 表示执行原方法
     */
    @Around("(method() || constructor()) && @annotation(trace)")    // 在连接点进行方法替换
    public Object aroundJoinPoint(final ProceedingJoinPoint joinPoint, Trace trace) throws Throwable {
        // 实例化计时器
        Watch watch = new Watch();
        // 开始计时
        watch.start();
        // 执行原方法
        Object result = joinPoint.proceed();
        // 结束计时
        watch.stop();

        // 获取方法信息对象
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取当前对象
        String className = methodSignature.getDeclaringType().getSimpleName();
        String methodName = methodSignature.getName();
        // 打印日志
        P.d(TAG + " --- className", buildLog(methodName, watch.getTotalTimeMillis()));
        return result;
    }

    /**
     * @return 消息
     */
    private String buildLog(String methodName, long totalTimeMillis) {
        return "Trace --> " +
                methodName +
                " [" +
                totalTimeMillis +
                "ms]";
    }
}
